﻿using System;
using System.Collections.Generic;
using System.Drawing;
using LuaVM.Core;

namespace Plugin.Window
{
    public class WindowPlugin : LuaDllPlugin
	{
		public override string Name => "Window";

		public override string[] ExportedVariables => new string[] {
			"WM_APP",
			"SW_HIDE",
			"SW_NORMAL",
			"SW_MINIMIZE",
			"SW_MAXIMIZE",
			"SW_SHOW",
			"SW_RESTORE"
		};		

		public override string[] ExportedFunctions => new string[] {
			"FindWindow",
			"IsWindow",
			"GetWindowText",
			"GetClassName",
			"GetAllWindows",
			"GetWindowRect",
			"GetClientRect",
			"WindowToScreen",
			"ClientToScreen",
			"GetForegroundWindow",
			"SetForegroundWindow",
			"IsMinimized",
			"IsMaximized",
			"IsWindowVisible",
			"SendMessage",
			"PostMessage"
		};

		protected override void OnAttach()
		{
			base.OnAttach();

			RegisterVariable("WM_APP", Win32API.Window.WM_APP);
			RegisterVariable("SW_HIDE", Win32API.Window.SW_HIDE);
			RegisterVariable("SW_NORMAL", Win32API.Window.SW_NORMAL);
			RegisterVariable("SW_MINIMIZE", Win32API.Window.SW_MINIMIZE);
			RegisterVariable("SW_MAXIMIZE", Win32API.Window.SW_MAXIMIZE);
			RegisterVariable("SW_SHOW", Win32API.Window.SW_SHOW);
			RegisterVariable("SW_RESTORE", Win32API.Window.SW_RESTORE);

			Lua["WM_APP"] = Win32API.Window.WM_APP;
			Lua["SW_HIDE"] = Win32API.Window.SW_HIDE;
			Lua["SW_NORMAL"] = Win32API.Window.SW_NORMAL;
			Lua["SW_MINIMIZE"] = Win32API.Window.SW_MINIMIZE;
			Lua["SW_MAXIMIZE"] = Win32API.Window.SW_MAXIMIZE;
			Lua["SW_SHOW"] = Win32API.Window.SW_SHOW;
			Lua["SW_RESTORE"] = Win32API.Window.SW_RESTORE;

			RegisterFunction<WindowPlugin>("FindWindow");
			RegisterFunction<WindowPlugin>("IsWindow");
			RegisterFunction<WindowPlugin>("GetWindowText");
			RegisterFunction<WindowPlugin>("GetClassName");
			RegisterFunction<WindowPlugin>(this, "GetAllWindows");
			RegisterFunction<WindowPlugin>("GetWindowRect");
			RegisterFunction<WindowPlugin>("GetClientRect");
			RegisterFunction<WindowPlugin>("WindowToScreen");
			RegisterFunction<WindowPlugin>("ClientToScreen");
			RegisterFunction<WindowPlugin>("GetForegroundWindow");
			RegisterFunction<WindowPlugin>("SetForegroundWindow");
			RegisterFunction<WindowPlugin>("IsMinimized");
			RegisterFunction<WindowPlugin>("IsMaximized");
			RegisterFunction<WindowPlugin>("IsWindowVisible");
			RegisterFunction<WindowPlugin>("SendMessage");
			RegisterFunction<WindowPlugin>("PostMessage");			
		}		

		// 允许在lua中FindWindow结果为nil
		public static IntPtr? FindWindow(string windowText, string className = null)
		{
			IntPtr hwnd = Win32API.Window.FindWindow(className, windowText);
			if (hwnd == IntPtr.Zero)
			{
				return null;
			}

			return hwnd;
		}

		// 允许在lua中使用nil为参数
		public static bool IsWindow(object hwnd)
		{
			if (hwnd == null)
			{
				return false;
			}

			return Win32API.Window.IsWindow((IntPtr)hwnd);
		}

		public static string GetWindowText(object hwnd)
		{
			if (!IsWindow(hwnd))
			{
				return null;
			}

			return Win32API.Window.GetWindowText((IntPtr)hwnd);
		}

		public static string GetClassName(object hwnd)
		{
			if (!IsWindow(hwnd))
			{
				return null;
			}

			return Win32API.Window.GetClassName((IntPtr)hwnd);
		}

		private List<IntPtr> m_enumWndList = null;

		/// <summary>
		/// 返回所有窗体
		/// </summary>
		/// <param name="visible">窗体必须可见</param>
		/// <returns>窗体列表</returns>
		public object GetAllWindows(bool visible = true)
		{
			m_enumWndList = new List<IntPtr>();
			Win32API.Window.EnumWindows(EnumCallBack, visible ? 1 : 0);
			LuaTable table = NewTable();

			table.CopyArray(m_enumWndList);
			return table.Table;
		}

		private bool EnumCallBack(IntPtr hwnd, int lParam)
		{
			if (lParam == 0 || Win32API.Window.IsWindowVisible(hwnd))
			{
				m_enumWndList.Add(hwnd);
			}
			
			return true;
		}

		// returns: left, top, width, height
		public static int? GetWindowRect(object hwnd, out int? top, out int? width, out int? height)
		{
			top = null;
			width = null;
			height = null;
			if (!IsWindow(hwnd))
			{
				return null;
			}

			try
			{
				Rectangle rect = Win32API.Window.GetWindowRect((IntPtr)hwnd);
				top = rect.Top;
				width = rect.Width;
				height = rect.Height;
				return rect.Left;
			}
			catch (Exception)
			{
				return null;
			}
		}

		public static int? GetClientRect(object hwnd, out int? top, out int? width, out int? height)
		{
			top = null;
			width = null;
			height = null;
			if (!IsWindow(hwnd))
			{
				return null;
			}

			try
			{
				Rectangle rect = Win32API.Window.GetClientRect((IntPtr)hwnd);				
				width = rect.Width;
				height = rect.Height;

				Point c = Win32API.Window.ClientToScreen((IntPtr)hwnd);
				top = c.Y;
				return c.X;
			}
			catch (Exception)
			{
				return null;
			}
		}		

		public static int? WindowToScreen(object hwnd, out int? y)
		{
			y = null;
			if (!IsWindow(hwnd))
			{
				return null;
			}

			Point pt = Win32API.Window.WindowToScreen((IntPtr)hwnd);
			y = pt.Y;
			return pt.X;
		}

		public static int? ClientToScreen(object hwnd, out int? y)
		{
			y = null;
			if (!IsWindow(hwnd))
			{
				return null;
			}

			Point pt = Win32API.Window.ClientToScreen((IntPtr)hwnd);
			y = pt.Y;
			return pt.X;
		}

		public static IntPtr? GetForegroundWindow()
		{
			IntPtr hwnd = Win32API.Window.GetForegroundWindow();
			if (hwnd == IntPtr.Zero)
			{
				return null;
			}

			return hwnd;
		}

		public static bool SetForegroundWindow(object hwnd)
		{
			if (!IsWindow(hwnd))
			{
				return false;
			}

			return Win32API.Window.SetForegroundWindow((IntPtr)hwnd);
		}

		public static bool IsMinimized(object hwnd)
		{
			if (!IsWindow(hwnd))
			{
				return false;
			}

			return Win32API.Window.IsMinimized((IntPtr)hwnd);
		}

		public static bool IsMaximized(object hwnd)
		{
			if (!IsWindow(hwnd))
			{
				return false;
			}

			return Win32API.Window.IsMaximized((IntPtr)hwnd);
		}

		public static bool IsWindowVisible(object hwnd)
		{
			if (!IsWindow(hwnd))
			{
				return false;
			}

			return Win32API.Window.IsWindowVisible((IntPtr)hwnd);
		}

		public static bool ShowWindow(object hwnd, int command)
		{
			if (!IsWindow(hwnd))
			{
				return false;
			}

			return Win32API.Window.ShowWindow((IntPtr)hwnd, command);
		}

		public static int? SendMessage(object hwnd, int msg, int wParam = 0, int lParam = 0)
		{
			if (!IsWindow(hwnd))
			{
				return null;
			}

			return Win32API.Window.SendMessage((IntPtr)hwnd, msg, wParam, lParam);
		}

		public static int? PostMessage(object hwnd, int msg, int wParam = 0, int lParam = 0)
		{
			if (!IsWindow(hwnd))
			{
				return null;
			}

			return Win32API.Window.PostMessage((IntPtr)hwnd, msg, wParam, lParam);
		}		
	}
}
